﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Workflow.ComponentModel.Compiler;
using System.Xml;
using OpenComposite.Base.Collections;
using OpenComposite.EII.CodeGenerators;
using OpenComposite.EII.Designer.Workflow;
using OpenComposite.EII.Interfaces;
using OpenComposite.Repository.Properties;
using OpenComposite.EII.Repository;
using OpenComposite.EII.Services;

namespace OpenComposite.EII.Forms
{
	internal partial class FormUpdateConfigValidateCompile : Form
	{
		#region Initialization
		private FormUpdateConfigValidateCompile()
		{
			InitializeComponent();
		}
		public FormUpdateConfigValidateCompile(
			IServiceProvider serviceProvider,
			BindingListEx<UpdateConfigurationItem> updateconfigitems,
			Dictionary<Repository.Workflow, WFConfig> workflowConfigs)
		{
			_serviceProvider = serviceProvider;
			_workflowConfigs = workflowConfigs;
			_updateconfigitems = updateconfigitems;

			_validateItems = new BindingListEx<_validateItem>();

			InitializeComponent();

			dgvWorkflows.AutoGenerateColumns = false;

			init();
		}
		private void init()
		{
			_taskListSvc = _serviceProvider.GetService(typeof(ITaskListService)) as ITaskListService;
			if ( _taskListSvc == null ) {
				_taskListSvc = new TaskErrorListService(_serviceProvider);
				foreach ( var wfc in _workflowConfigs ) {
					_validateItem vi = new _validateItem(wfc.Key, wfc.Value);
					_validateItems.Add(vi);
					Control c = _taskListSvc.GetView(wfc.Key) as Control;
					if ( c != null ) {
						c.Visible = false;
						c.Dock = DockStyle.Fill;
						pnlTaskView.Controls.Add(c);
						vi.TaskList = c;
					}
				}
			}
			dgvWorkflows.DataSource = _validateItems;
		}
		#endregion

		#region Private Members

		#region Fields
		private IServiceProvider _serviceProvider = null;
		private ITaskListService _taskListSvc = null;

		private Dictionary<Repository.Workflow, WFConfig> _workflowConfigs;
		private BindingListEx<UpdateConfigurationItem> _updateconfigitems;
		private BindingListEx<_validateItem> _validateItems;
		#endregion

		#region Methods

		#region Validation
		private void doValidation()
		{
			foreach ( var item in _workflowConfigs ) {
				try {
					_validateItem vitem = _validateItems.Find(vi => vi.Workflow == item.Key);

					WorkflowCodeGenerator wcg = new WorkflowCodeGenerator(vitem.Workflow, vitem.Workflow, _taskListSvc);
					wcg.GenerateCode();
					vitem.WFCodeGenerator = wcg;

					WorkflowValidator wfv = new WorkflowValidator(_serviceProvider);
					wfv.TaskListService = _taskListSvc;
					wfv.ValidationCompleted += new EventHandler(wfv_ValidationCompleted);
					wfv.DoValidation(item.Key);
				} catch ( Exception ex ) {
					if ( _taskListSvc != null ) {
						_taskListSvc.Add(item.Key, TaskItemType.Error, TaskStatus.None, item, ex.Message);
					} else {
						Trace.WriteLine(ex.ToString());
					}
				}
			}
		}

		void wfv_ValidationCompleted(object sender, EventArgs e)
		{
			WorkflowValidator wfv = sender as WorkflowValidator;
			if ( wfv == null ) return;

			wfv.ValidationCompleted -= new EventHandler(wfv_ValidationCompleted);
			_validateItem vitem = _validateItems.Find(vi => vi.Workflow == wfv.Workflow);
			if ( vitem == null ) return;

			if ( wfv.HasTasks ) {
				vitem.Status = _validateItem.Todo;
			} else if ( wfv.HasUnknown ) {
				vitem.Status = _validateItem.Unknown;
			} else {
				vitem.Status = _validateItem.OK;
			}
			_validateItems.ResetItem(_validateItems.IndexOf(vitem));

			WorkflowCodeGenerator wcg = vitem.WFCodeGenerator;
			if ( wcg != null ) {
				wcg = new WorkflowCodeGenerator(vitem.Workflow, vitem.Workflow, _taskListSvc);
			}
			try {
				WorkflowCompilerResults results = wcg.CompileCode();
				if ( results.Errors.HasErrors ) {
					vitem.Status = _validateItem.Error;
				} else if ( results.Errors.HasWarnings ) {
					vitem.Status = _validateItem.Warning;
				}
			} catch ( Exception ex ) {
				_taskListSvc.Add(vitem.Workflow, TaskItemType.Error, TaskStatus.None, vitem, ex.Message);
				vitem.Status = _validateItem.Error;
			}
			_validateItems.ResetItem(_validateItems.IndexOf(vitem));
		}
		#endregion

		#region Update / Save Configs
		/// <summary>
		/// Updates the configs.
		/// </summary>
		private void updateConfigs()
		{
			StringBuilder msg = null;
			CompilerResults compilerresults;
			List<BusinessObject> bobjects = new List<BusinessObject>();
			List<RepositoryBusinessRule> brules = new List<RepositoryBusinessRule>();
			List<WebService> webservices = new List<WebService>();
			List<LogicalActivity> logicalactivities = new List<LogicalActivity>();
			List<Repository.HumanActivity> humanactivities = new List<Repository.HumanActivity>();

			try {
				foreach ( var item in _workflowConfigs ) {
					var workflow = item.Key;
					var wfUCI = getUpdateConfigItemByItem(workflow);
					if ( wfUCI == null || !wfUCI.DoUpdate ) continue;
					var wfconfig = item.Value;

					bobjects.Clear();
					brules.Clear();
					webservices.Clear();
					logicalactivities.Clear();
					humanactivities.Clear();

					bool updateBObjs = false;
					bool updateBRules = false;
					bool updateEPoints = true; // TODO: change to false if change detection is working
					bool updateLogical = true; // TODO: change to false if change detection is working

					#region update repository items
					foreach ( var repitem in wfconfig.RepositoryItems ) {
						WebService ws = repitem as WebService;
						RuleConstant rc = repitem as RuleConstant;
						Repository.HumanActivity ha = repitem as Repository.HumanActivity;
						BusinessObject bo = repitem as BusinessObject;
						RepositoryBusinessRule brule = repitem as RepositoryBusinessRule;
						LogicalActivity la = repitem as LogicalActivity;

						if ( ws != null ) {
							webservices.Add(ws);
						} else if ( ha != null ) {
							humanactivities.Add(ha);
						} else if ( bo != null ) {
							bobjects.Add(bo);
						} else if ( brule != null ) {
							brules.Add(brule);
						} else if ( la != null ) {
							logicalactivities.Add(la);
						}

						UpdateConfigurationItem uci = getUpdateConfigItemByItem(repitem);
						if ( uci == null /*|| !uci.DoUpdate */) continue;

						#region Update Xml Configs
						foreach ( var xmlConfig in wfconfig.XmlConfigs ) {
							if ( ws != null ) {
								var sectiongroup = xmlConfig.SectionGroups["system.serviceModel"];
								if ( sectiongroup == null ) continue;
								var section = sectiongroup.Sections["client"] as System.ServiceModel.Configuration.ClientSection;
								if ( section == null ) continue;
								foreach ( System.ServiceModel.Configuration.ChannelEndpointElement endpoint in section.Endpoints ) {
									if ( ws.InterfaceFullName == endpoint.Contract ) {
										endpoint.Address = new Uri(ws.Uri);
									}
								}
							}
							if ( rc != null ) {
								string key = "CONST_" + rc.Name;
								var element = xmlConfig.AppSettings.Settings[key];
								if ( element != null ) {
									element.Value = rc.Value;
								} else {
									xmlConfig.AppSettings.Settings.Add(key, rc.Value);
								}
							} else if ( ha != null ) {
								string key = ha.Name + "Url";
								var element = xmlConfig.AppSettings.Settings[key];
								if ( element != null ) {
									element.Value = ha.Url.ToString();
								} else {
									xmlConfig.AppSettings.Settings.Add(key, ha.Url.ToString());
								}
							}
						}
						#endregion

						#region Update Assembly Configs
						if ( bo != null ) {
							if ( !updateBObjs ) updateBObjs = true;
						} else if ( brule != null ) {
							if ( !updateBRules ) updateBRules = true;
						} else if ( ws != null || ha != null ) {
							if ( !updateEPoints ) updateEPoints = true;
						}
						#endregion
					}
					#endregion
					#region generate assemblies
					string pathTmp = Path.Combine(Path.Combine(Path.GetTempPath(), "OpenComposite"), workflow.Name);
					if ( !Directory.Exists(pathTmp) ) Directory.CreateDirectory(pathTmp);
					string configFile = Path.Combine(pathTmp, "app.config");
					if ( File.Exists(configFile) ) {
						File.Delete(configFile);
					}

					ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
					fileMap.ExeConfigFilename = configFile;

					Configuration webconfig = ConfigurationManager
						.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
					webconfig.NamespaceDeclared = true;
					webconfig.Save(ConfigurationSaveMode.Minimal, true);

					foreach ( var asmConfigPath in wfconfig.AssemblyConfigPaths ) {
						// create backup
						File.Copy(
							asmConfigPath,
							asmConfigPath + "." + DateTime.Now.ToString("yyyyMMddHHmmss") + ".backup",
							true);

						compilerresults = null;
						if ( asmConfigPath.Contains(".BusinessObjects.") && updateBObjs ) {
							// generate business objects assembly
							compilerresults = generateBusinessObjectAssembly(asmConfigPath, workflow, bobjects);
						} else if ( asmConfigPath.Contains(".Rules.") && updateBRules ) {
							// generate business rules assembly
							compilerresults = generateBusinessRuleAssembly(asmConfigPath, workflow, brules);
						} else if ( asmConfigPath.Contains(".Endpoints.") && updateEPoints ) {
							// generate endpoints assembly
							compilerresults = generateEndpointAssembly(
								asmConfigPath, workflow, webservices, logicalactivities, webconfig);
						} else if ( asmConfigPath.Contains(".Logical.") && updateLogical ) {
							// generate logical-physical assembly
							compilerresults = generateLogicalAssembly(asmConfigPath, workflow);
						} else if ( asmConfigPath.EndsWith(workflow.Name + ".dll") ) {
							// generate process assembly
							compilerresults = generateProcessAssembly(asmConfigPath, workflow);
						}
						if ( compilerresults != null && compilerresults.Errors.HasErrors ) {
							if ( msg == null ) msg = new StringBuilder(); else msg.Length = 0;
							foreach ( CompilerError error in compilerresults.Errors ) {
								if ( !error.IsWarning ) msg.AppendLine(error.ErrorText);
							}
							MessageBox.Show(msg.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
							return;
						}
					}
					// save main dll
					//string asmPath = 
					//File.Copy(
					//    asmConfigPath,
					//    asmConfigPath + "." + DateTime.Now.ToString("yyyyMMddHHmmss") + ".backup",
					//    true);

					#endregion
					#region update mapping item
					foreach ( var mapitem in wfconfig.MappingItems ) {
						UpdateConfigurationItem uci = getUpdateConfigItemByItem(mapitem);
						if ( uci == null || !uci.DoUpdate ) continue;

						string key = "";
						if ( mapitem.Property == "Mapping" ) {
							key = Deployment.cxsltInputSubscription;
						} else if ( mapitem.Property == "Input Mapping" ) {
							key = Deployment.cxsltInput;
						} else if ( mapitem.Property == "Output Mapping" ) {
							key = Deployment.cxsltOutput;
						}
						key += mapitem.Element;
						foreach ( var xmlConfig in wfconfig.XmlConfigs ) {
							var element = xmlConfig.AppSettings.Settings[key];
							if ( element != null ) element.Value = mapitem.Xslt.InnerXml;
						}
					}
					#endregion
					#region save configurations
					foreach ( var config in wfconfig.XmlConfigs ) {
						config.Save(ConfigurationSaveMode.Minimal);
						// update config with new values
						string newCXml = File.ReadAllText(configFile);
						string oldCXml = File.ReadAllText(config.FilePath);
						newCXml = newCXml.Replace(" xmlns=\"http://schemas.microsoft.com/.NetConfiguration/v2.0\"", "");
						oldCXml = oldCXml.Replace(" xmlns=\"http://schemas.microsoft.com/.NetConfiguration/v2.0\"", "");
						XmlDocument xdNewConfig = new XmlDocument();
						xdNewConfig.LoadXml(newCXml);
						XmlDocument xdConfig = new XmlDocument();
						xdConfig.LoadXml(oldCXml);
						// append system.serviceModel
						XmlNode xnOldSvcModel = xdConfig.SelectSingleNode("//system.serviceModel");
						if ( xnOldSvcModel != null ) {
							XmlElement xeOldBindungs = xnOldSvcModel["bindings"];
							XmlElement xeOldClient = xnOldSvcModel["client"];
							if ( xeOldBindungs != null ) {
								List<XmlNode> removable = new List<XmlNode>();
								foreach ( XmlNode xnBChild in xeOldBindungs.ChildNodes ) {
									if ( xnBChild.SelectSingleNode("./binding[@name='bindConfig']") == null ) {
										removable.Add(xnBChild);
									}
								}
								foreach ( XmlNode xnRemove in removable ) {
									xeOldBindungs.RemoveChild(xnRemove);
								}
							}
							if ( xeOldClient != null ) xnOldSvcModel.RemoveChild(xeOldClient);
						}
						XmlNode xnServiceModel = xdNewConfig.SelectSingleNode("//system.serviceModel");
						if ( xnServiceModel != null ) {
							if ( xnOldSvcModel == null ) {
								xnOldSvcModel = xdConfig.DocumentElement.AppendChild(xdConfig.CreateElement("system.serviceModel"));
							}
							if ( xnServiceModel["bindings"] != null ) {
								if ( xnOldSvcModel["bindings"] == null ) {
									xnOldSvcModel.AppendChild(xdConfig.ImportNode(xnServiceModel["bindings"], true));
								} else {
									foreach ( XmlNode xn in xnServiceModel["bindings"].ChildNodes ) {
										XmlNode xnImported = xdConfig.ImportNode(xn, true);
										addTimeoutsToBindings(xdConfig, xnImported);
										xnOldSvcModel["bindings"].AppendChild(xnImported);
									}
								}
							}
							if ( xnServiceModel["client"] != null ) {
								xnOldSvcModel.AppendChild(xdConfig.ImportNode(xnServiceModel["client"], true));
							}
						}
						// append appSettings
						XmlNode xnAppSettings = xdNewConfig.SelectSingleNode("//appSettings");
						if ( xnAppSettings != null ) {
							if ( xdConfig.DocumentElement["appSettings"] == null ) {
								xdConfig.DocumentElement.AppendChild(xdConfig.CreateElement("appSettings"));
							}
							foreach ( XmlNode xn in xnAppSettings.ChildNodes ) {
								XmlNode xnOld = xdConfig.DocumentElement["appSettings"]
									.SelectSingleNode("./add[@key=\"" + xn.Attributes["key"].Value + "\"]");
								if ( xnOld != null ) xdConfig.DocumentElement["appSettings"].RemoveChild(xnOld);
								xdConfig.DocumentElement["appSettings"].AppendChild(xdConfig.ImportNode(xn, true));
							}
						}
						//xdConfig.Save(webconfigpath);
						XmlWriterSettings settings = new XmlWriterSettings();
						settings.Indent = true;
						settings.IndentChars = "\t";
						settings.NewLineOnAttributes = true;
						using ( XmlWriter xw = XmlTextWriter.Create(config.FilePath, settings) ) {
							xdConfig.WriteTo(xw);
							xw.Flush();
						}
					}
					try { File.Delete(configFile); } catch { }
					#endregion
				}
				MessageBox.Show("Update Configurations completed successfully.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		private void addTimeoutsToBindings(XmlDocument xdConfig, XmlNode xn)
		{
			XmlNodeList xnlBinding = xn.SelectNodes("./binding");
			if ( xnlBinding == null || xnlBinding.Count == 0 ) return;
			foreach ( XmlNode xnBinding in xnlBinding ) {
				addAttributeIfNotExists(xdConfig, xnBinding, "openTimeout", "00:01:00");
				addAttributeIfNotExists(xdConfig, xnBinding, "receiveTimeout", "24.00:00:00");
				addAttributeIfNotExists(xdConfig, xnBinding, "sendTimeout", "24.00:00:00");
				addAttributeIfNotExists(xdConfig, xnBinding, "closeTimeout", "00:01:00");
			}
		}
		private static void addAttributeIfNotExists(
			XmlDocument xdConfig, XmlNode xn, string name, string value)
		{
			if ( xn.Attributes[name] == null ) {
				xn.Attributes.Append(xdConfig.CreateAttribute(name)).Value = value;
			}
		}

		/// <summary>
		/// Gets the update config item by item.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns></returns>
		private UpdateConfigurationItem getUpdateConfigItemByItem(object item)
		{
			return _updateconfigitems.Find(delegate(UpdateConfigurationItem updateitem)
			{
				if ( updateitem != null && updateitem.Item != null ) {
					return updateitem.Item.Equals(item);
				} else {
					return false;
				}
			});
		}

		/// <summary>
		/// Generates the business object assembly.
		/// </summary>
		/// <param name="assemblyPath">The assembly path.</param>
		/// <param name="workflow">The workflow.</param>
		/// <param name="bobjects">The business objects.</param>
		/// <returns>The <see cref="T:System.CodeDom.Compiler.CompilerResults"/>.</returns>
		private CompilerResults generateBusinessObjectAssembly(
			string assemblyPath, Repository.Workflow workflow,
			ICollection<BusinessObject> bobjects)
		{
			if ( workflow == null ) throw new ArgumentNullException("workflow");
			if ( bobjects == null || bobjects.Count == 0 ) return null;	// no business objects

			Deployment deploy = new Deployment(workflow);
			CodeCompileUnit ccuData;
			CodeNamespace cnsData;

			#region create data code compile unit
			ccuData = new CodeCompileUnit();
			cnsData = new CodeNamespace(workflow.ExtendedProperties.NamespaceData);
			cnsData.Imports.Add(new CodeNamespaceImport("System"));
			cnsData.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
			cnsData.Imports.Add(new CodeNamespaceImport("System.Text"));
			cnsData.Imports.Add(new CodeNamespaceImport("System.Xml.Serialization"));
			ccuData.Namespaces.Add(cnsData);
			#endregion

			foreach ( var bo in bobjects ) {
				deploy.AddBusinessObjectToDataCode(ccuData, workflow.ExtendedProperties.NamespaceData, null,
					bo, workflow.GenerateDataContract);
			}
#if DEBUG
			string code = CSharpGenerator.GenerateCodeString(ccuData);
#endif
			return CSharpGenerator.CompileCodeToAssembly(
				assemblyPath, new CodeCompileUnit[] { ccuData }, null);
		}
		/// <summary>
		/// Generates the business rule assembly.
		/// </summary>
		/// <param name="assemblyPath">The assembly path.</param>
		/// <param name="workflow">The workflow.</param>
		/// <param name="brules">The business rules.</param>
		/// <returns>The <see cref="T:System.CodeDom.Compiler.CompilerResults"/>.</returns>
		private CompilerResults generateBusinessRuleAssembly(
			string assemblyPath, Repository.Workflow workflow,
			ICollection<RepositoryBusinessRule> brules)
		{
			if ( workflow == null ) throw new ArgumentNullException("workflow");
			if ( brules == null || brules.Count == 0 ) return null;	// no business rules

			Deployment deploy = new Deployment(workflow);
			CodeCompileUnit ccuBRules;
			CodeNamespace cnsBRules;
			CodeTypeDeclaration ctdBRules;

			#region create rules code compile unit
			ccuBRules = new CodeCompileUnit();
			cnsBRules = new CodeNamespace(workflow.ExtendedProperties.NamespaceRules);
			cnsBRules.Imports.Add(new CodeNamespaceImport("System"));
			cnsBRules.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
			cnsBRules.Imports.Add(new CodeNamespaceImport("System.Text"));
			ccuBRules.Namespaces.Add(cnsBRules);
			ctdBRules = new CodeTypeDeclaration("Rules");
			ctdBRules.Attributes = MemberAttributes.Public;
			ctdBRules.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(SerializableAttribute))));
			cnsBRules.Types.Add(ctdBRules);
			#endregion

			List<int> bruleids = new List<int>();
			List<int> ruleconstids = new List<int>();
			foreach ( var br in brules ) {
				deploy.AddRuleToCodeCompileUnit(br, ccuBRules, workflow, bruleids, ruleconstids);
			}

			List<RuleConstant> ruleconstants = new List<RuleConstant>();
			foreach ( var rcid in ruleconstids ) {
				RuleConstant rc = Global.Data.RepositoryService.GetItem<RuleConstant>(rcid);
				if ( rc != null && !ruleconstants.Contains(rc) ) ruleconstants.Add(rc);
			}
			deploy.GenerateRuleConstantsCode(ctdBRules, workflow, null, ruleconstants);

			return CSharpGenerator.CompileCodeToAssembly(
				assemblyPath, new CodeCompileUnit[] { ccuBRules }, null);
		}
		/// <summary>
		/// Generates the endpoint assembly.
		/// </summary>
		/// <param name="assemblyPath">The assembly path.</param>
		/// <param name="workflow">The workflow.</param>
		/// <param name="webservices">The web services.</param>
		/// <param name="logicalactivities">The logical activities.</param>
		/// <param name="configuration">The configuration.</param>
		/// <returns>
		/// The <see cref="T:System.CodeDom.Compiler.CompilerResults"/>.
		/// </returns>
		private CompilerResults generateEndpointAssembly(
			string assemblyPath, Repository.Workflow workflow,
			ICollection<WebService> webservices,
			ICollection<LogicalActivity> logicalactivities,
			Configuration configuration)
		{
			if ( workflow == null ) throw new ArgumentNullException("workflow");
			if ( ( webservices == null && logicalactivities == null ) ||
				 ( webservices.Count == 0 && logicalactivities.Count == 0 ) ) {
				return null; // no endpoints
			}

			Deployment deploy = new Deployment(workflow);
			CodeCompileUnit ccuContracts = new CodeCompileUnit();
			ServiceGenerationType currentSvcGenType;
			TypeProvider typeProvider;
			CodeTypeReference typeRefLogActContract;
			string contractnspace;
			System.Web.Services.Description.ServiceDescription wsdl;
			Collection<System.ServiceModel.Channels.Binding> bindings;
			string endpointConfigName;

			foreach ( var ws in webservices ) {
				typeProvider = null;
				typeRefLogActContract = null;
				contractnspace = workflow.ExtendedProperties.NamespaceBase;
				wsdl = null;
				bindings = null;

				if ( ws.Type == WebService.ServiceType.APIService ) {
					currentSvcGenType = ServiceGenerationType.APIService;
				} else if ( ws.Uri.ToLower().EndsWith(".asmx") ) {
					currentSvcGenType = ServiceGenerationType.SystemWebService;
				} else if ( ws.Uri.ToLower().EndsWith(".svc") ) {
					currentSvcGenType = ServiceGenerationType.WCFService;
				} else {
					currentSvcGenType = ServiceGenerationType.Unknown;
				}
				deploy.GenerateWebMethodContract(currentSvcGenType, ref typeProvider,
					ref typeRefLogActContract, ws, ref contractnspace, ref wsdl, ref bindings, null,
					out endpointConfigName, configuration, ccuContracts);
			}
			foreach ( var logact in logicalactivities ) {
				contractnspace = "";
				deploy.GenerateHumanActivityContract(ccuContracts, null, logact, out contractnspace);
			}
			return CSharpGenerator.CompileCodeToAssembly(
				assemblyPath, new CodeCompileUnit[] { ccuContracts }, null);
		}
		/// <summary>
		/// Generates the logical assembly.
		/// </summary>
		/// <param name="assemblyPath">The assembly path.</param>
		/// <param name="workflow">The workflow.</param>
		/// <returns></returns>
		private CompilerResults generateLogicalAssembly(
			string assemblyPath, Repository.Workflow workflow)
		{
			WorkflowCodeGenerator codegen = new WorkflowCodeGenerator(workflow, workflow);
			codegen.GenerateCode();

			string fileBObjects = assemblyPath.Replace(".Logical.config.", ".BusinessObjects.config.");
			string fileEndpoints = assemblyPath.Replace(".Logical.config.", ".Endpoints.config.");
			string fileRules = assemblyPath.Replace(".Logical.config.", ".Rules.config.");

			return CSharpGenerator.CompileCodeToAssembly(
				assemblyPath,
				new CodeCompileUnit[] {
					codegen.CodeWorkflowInterface,
					codegen.CodeServices,
					codegen.CodeMapping
				},
				new string[] {
					fileBObjects,
					fileEndpoints,
					fileRules
				}
			);
		}

		private WorkflowCompilerResults generateProcessAssembly(
			string assemblyPath, Repository.Workflow workflow/*, WorkflowCodeGenerator codegen*/)
		{
			WorkflowCompilerResults results = null;
			string wfname = RepositoryService.GetIdentifier(workflow.Name);
			string pathTmp = Path.Combine(Path.Combine(Path.GetTempPath(), "OpenComposite"), wfname);

			if ( !Directory.Exists(pathTmp) ) Directory.CreateDirectory(pathTmp);

			string path = Path.GetDirectoryName(assemblyPath);
			string fileBObjects = Path.Combine(path, workflow.Name + ".BusinessObjects.config.dll");
			string fileEndpoints = Path.Combine(path, workflow.Name + ".Endpoints.config.dll");
			string fileRules = Path.Combine(path, workflow.Name + ".Rules.config.dll");
			string fileLogical = Path.Combine(path, workflow.Name + ".Logical.config.dll");

			try {
				StringBuilder errors = new StringBuilder();
				using ( new OpenComposite.Base.WaitMouse() ) {
					List<string> files = this.saveFilesForProcessAssembly(pathTmp, workflow);

					// Check for code beside file and rules file

					WorkflowCompiler compiler = new WorkflowCompiler();
					WorkflowCompilerParameters parameters = new WorkflowCompilerParameters();
					parameters.ReferencedAssemblies.Add(typeof(System.EventHandler).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.ComponentModel.AttributeCollection).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Workflow.ComponentModel.CompositeActivity).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Workflow.Activities.SequentialWorkflowActivity).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(OpenComposite.Workflow.LogicalEPActivity).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Runtime.Serialization.XsdDataContractImporter).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.ServiceModel.EndpointAddress).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Web.Services.WebService).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Xml.XmlDocument).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(OpenComposite.Base.Structures.RepositoryInformationAttribute).Assembly.Location);
					parameters.ReferencedAssemblies.Add(typeof(System.Configuration.ConfigurationManager).Assembly.Location);
					parameters.ReferencedAssemblies.Add(fileBObjects);
					parameters.ReferencedAssemblies.Add(fileEndpoints);
					parameters.ReferencedAssemblies.Add(fileRules);
					parameters.ReferencedAssemblies.Add(fileLogical);

					parameters.OutputAssembly = assemblyPath;

					string rulesFile = "";
					foreach ( string file in files ) {
						if ( file.ToLower().EndsWith(".rules") )
							rulesFile = file;
					}
					if ( files.Contains(rulesFile) ) files.Remove(rulesFile);

					if ( File.Exists(rulesFile) ) {
						// adding the rules file to the resources
						if ( parameters.CompilerOptions != null && parameters.CompilerOptions.Length > 0 ) {
							parameters.CompilerOptions += " ";
						}
						string resources = "/resource:\"" + rulesFile + "\"," +
							workflow.ExtendedProperties.NamespaceCode + "." +
							workflow.Name + "." + "rules";
						parameters.CompilerOptions += resources;
					}
					// Compile the workflow
					results = compiler.Compile(parameters, files.ToArray());

					foreach ( CompilerError compilerError in results.Errors ) {
						string file = Path.GetFileName(compilerError.FileName);
						errors.AppendLine(string.Format("{0}: Line {1}: {2}", file, compilerError.Line, compilerError.ErrorText));
					}
					parameters.TempFiles.Delete();
					results.TempFiles.Delete();
				}
				if ( errors.Length != 0 ) {
					MessageBox.Show(errors.ToString(), "Compile Orchestration", MessageBoxButtons.OK, MessageBoxIcon.Error);
				} else {
					MessageBox.Show(
						"Orchestration compiled successfully."/* Compiled assembly: \n" + results.CompiledAssembly.GetName()*/
						, "Compile Orchestration", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, "Compile Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
			} finally {
			}
			return results;
		}
		public List<string> saveFilesForProcessAssembly(string path, Repository.Workflow workflow)
		{
			string file;
			List<string> files = new List<string>();

			file = Path.Combine(path, "Orchestration1.xoml");
			writeStringToFile(file, workflow.XOML);
			files.Add(file);

			file = Path.Combine(path, "Orchestration1.xoml.cs");
			writeStringToFile(file, workflow.CodeBeside);
			files.Add(file);

			file = Path.Combine(path, "Orchestration1.rules");
			writeStringToFile(file, workflow.Rules);
			files.Add(file);

			file = Path.Combine(path, "IOrchestration.cs");
			writeStringToFile(file, workflow.CodeInterface);
			files.Add(file);

			file = Path.Combine(path, "LocalService.cs");
			writeStringToFile(file, workflow.CodeLocalService);
			files.Add(file);

			file = Path.Combine(path, "LogicalActivities.cs");
			writeStringToFile(file, workflow.CodeLogicalActivity);
			files.Add(file);

			return files;
		}
		private void writeStringToFile(string filepath, string content)
		{
			using ( FileStream fs = File.Create(filepath) ) { }
			File.SetAttributes(filepath, FileAttributes.Temporary);
			File.WriteAllText(filepath, content);
		}

		#endregion

		#endregion

		#region Event Handlers
		private void dgvWorkflows_SelectionChanged(object sender, EventArgs e)
		{
			if ( dgvWorkflows.SelectedRows.Count != 1 ) return;

			_validateItem vi = dgvWorkflows.SelectedRows[0].DataBoundItem as _validateItem;
			if ( vi != null && vi.TaskList != null ) {
				vi.TaskList.Visible = true;
				vi.TaskList.BringToFront();
			}
		}
		private void FormUpdateConfigValidateCompile_Shown(object sender, EventArgs e)
		{
			this.Refresh();
			doValidation();
		}
		private void tsbtnUpdate_Click(object sender, EventArgs e)
		{
			this.Refresh();
			string processes = "";
			foreach ( var item in _validateItems ) {
				if ( item.Status != _validateItem.OK ) {
					if ( processes.Length > 0 ) processes += Environment.NewLine;
					processes += " - " + item.Workflow.Name;
				}
			}
			if ( !string.IsNullOrEmpty(processes) ) {
				string msg = string.Format(
					"The following processes or composites contain errors/warnings:{0}{0}{1}{0}{0}" +
					"Therefore the update may NOT work correctly.{0}" +
					"Do you want to ignore these errors/warnings and continue with the update?",
					Environment.NewLine, processes);
				if ( MessageBox.Show(msg, this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2)
					 == DialogResult.No ) {
					return;
				}
			}
			updateConfigs();
			this.Close();
		}
		#endregion

		#endregion

		#region Private Class: _validateItem
		private class _validateItem
		{
			public _validateItem(Repository.Workflow workflow, WFConfig wfconfig)
			{
				this.Workflow = workflow;
				this.WFConfig = wfconfig;
				this.Status = _validateItem.Unknown;
			}

			public Repository.Workflow Workflow { get; private set; }
			public WFConfig WFConfig { get; private set; }
			public WorkflowCodeGenerator WFCodeGenerator { get; set; }

			public Image Status { get; set; }
			public string WFName { get { return this.Workflow != null ? this.Workflow.Name : ""; } }

			public Control TaskList { get; set; }

			public static Image OK = Resources.status_ok;
			public static Image Unknown = Resources.status_unknown;
			public static Image Todo = Resources.status_error;
			public static Image Warning = Resources.warning_10;
			public static Image Error = Resources.cancel_10;
		}
		#endregion
	}
}
